package com.miyake.dsp.common;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;

import com.miyake.dsp.common.EdgeDetector.Edge;
import com.miyake.dsp.osa.lab.Sort;

public class EdgeDetector {

	public double[] getDiff() {
		return diff;
	}

	public List<Double> getThresholds() {
		return thresholds;
	}

	private double[] x;
	private double[] y;
	private double[] ex;
	private double[] ey;
	private double[] diff;
	private List<Double> trList = new ArrayList<Double>();
	private List<Double> angles = new ArrayList<Double>();
	private List<Integer> indexes = new ArrayList<Integer>();
	private List<Double> thresholds = new ArrayList<Double>();
	
	private Edge edge;
	
	public enum Edge{
		Rise, Fall, All
	}
	public EdgeDetector(Edge edge, double[] x, double[] y) {
		this.x = x;
		this.y = y;
		this.edge = edge;
	}
	
	
	public synchronized void process() {
		if (false) {
			newAlgo();
		}
		else {
			oldAlgo();
		}
		
		int i = 0;
		ex = new double[indexes.size()];
		ey = new double[indexes.size()];
		
		for (Integer index : indexes) {
			ex[i] = x[index];
			ey[i] = y[index];
			i++;
		}
	}

	
	private double getFloorLevel(double[] data, int startIndex) {
		Sort sort = new Sort(data);
		double[] d = sort.getOuty();
		//int length = d.length / 100;
		double[] smoothed = d;//CommonTool.getSmoothed(d, length);
		double[] diff = CommonTool.getDiff(smoothed);
		double threshold = CommonTool.getMaxValue(diff, 0, diff.length/100) * 2.0;
		for (int i = startIndex; i < diff.length; i++) {
			if (diff[i] > threshold) {
				return data[i];
			}
		}
		return data[0];
	}
	
	private enum Status {
		TurnToRise, TurnToFall, Rising, Falling, Neutral
	}
	private void newAlgo() {
		double[] smoothed = CommonTool.getSmoothed(y, y.length/200);
		diff = CommonTool.getDiff(smoothed);
		Status status = Status.Neutral;
		double threshold = getFloorLevel(diff, 0);
		thresholds.add(threshold);
		for (int i = 0; i < diff.length; i++) {
			if (diff[i] > threshold) {
				if (status.equals(Status.Falling)) {
					indexes.add(i);
					status = Status.TurnToRise;
				}
				else if (status.equals(Status.Rising)) {
					status = Status.Rising;
				}
				else if (status.equals(Status.Neutral)) {
					indexes.add(i);
					status = Status.TurnToRise;
				}
				else if (status.equals(Status.TurnToRise)) {
					status = Status.Rising;
				}
				else if (status.equals(Status.TurnToFall)) {
					status = Status.TurnToRise;
				}
			}
			else {
				if (status.equals(Status.Falling)) {
						status = Status.Falling;
				}
				else if (status.equals(Status.Rising)) {
					threshold = getFloorLevel(diff, i+1);
					thresholds.add(threshold);
					indexes.add(i+1);
					status = Status.TurnToFall;
				}
				else if (status.equals(Status.Neutral)) {
					indexes.add(i);
					status = Status.TurnToFall;
				}
				else if (status.equals(Status.TurnToRise)) {
					indexes.add(i);
					status = Status.TurnToFall;
				}
				else if (status.equals(Status.TurnToFall)) {
					status = Status.Falling;
				}
			}
		}
	}

	private void oldAlgo() {
		diff = CommonTool.getDiff(y);
		
		int startPoint = 0;
		int rising = 0;
		double coef = 1.0;
		diff = CommonTool.getSmoothed(diff, diff.length/100);
		double threshold = CommonTool.getMaxValue(diff, diff.length/100, diff.length/50);
		thresholds.add(threshold);
		for (int k = 0; k < 10; k++) {
			for (int i = 0; i < diff.length; i++) {
				if (diff[i] > threshold) {
					if (rising == 0) {
						indexes.add(i);
						startPoint = i;	
					}
					rising++;
				}
				else {
					if (rising > 0) {
						angles.add(y[i]-y[startPoint]);
						
					}
					rising = 0;
				}
			}
			if (rising > 0) {
				angles.add(y[y.length-1] - y[startPoint]);
			}
			
			if (indexes.size() > 3) {
				threshold *= 1.2;
				angles.clear();
				indexes.clear();
			}
			else {
				break;
			}
		}
		
	}

	public double[] getX() {
		return ex;
	}
	
	public double[] getY() {
		return ey;
	}
	
	public double[] getRisingTime() {
		return ArrayUtils.toPrimitive(trList.toArray(new Double[0]));
	}
	
	public double[] getDiffValues() {
		return ArrayUtils.toPrimitive(angles.toArray(new Double[0]));
	}
	
	public int[] getIndexes() {
		return ArrayUtils.toPrimitive(indexes.toArray(new Integer[0]));
	}
	

}
